﻿//-----------------------------------------------------------------------
// <copyright file="ModelFactory.cs" company="Redmist">
//     Copyright (c) Damien T Smith. All rights reserved.
// </copyright>
// <author>Damien Smith</author>
//-----------------------------------------------------------------------

namespace Redmist.UsageMonitor.Model
{
    using System;
    using Redmist.UsageMonitor.CustomControls;
    using Redmist.UsageMonitor.UsageData;

    /// <summary>A factory for generating usage models.</summary>
    public static class ModelFactory
    {
        /// <summary>Creates a usage model using the supplied parameters.</summary>
        /// <param name="monthlyUsage">A Redmist.UsageMonitor.UsageData.MonthlyUsage representing the basic usage data.</param>
        /// <param name="planType">A Redmist.UsageMonitor.CustomControls.PlanType representing a type of ISP plan.</param>
        /// <param name="quotaRatio">A value indicating the reduced rate of off peak usage.</param>
        /// <param name="fixedOffPeakQuota">A value representing the off peak quota in megabytes for a fixed off peak plan.</param>
        /// <param name="graphCountedOffPeak">A value indicating whether off peak graph data should be shown as counted.</param>
        /// <param name="commonScale">A value indicating whether download and uploads graphs should share a common scale.</param>
        /// <param name="historyView">A Redmist.UsageMonitor.Model.HistoryView representing the usage history graph style.</param>
        /// <param name="historyDays">A value representing the number of days of usage history to show on history graphs.</param>
        /// <returns>A Redmist.UsageMonitor.Model.IUsageModel representing a monthly usage model.</returns>
        public static IUsageModel GenerateUsageModel(MonthlyUsage monthlyUsage, PlanType planType, int quotaRatio, int fixedOffPeakQuota, bool graphCountedOffPeak, bool commonScale, HistoryView historyView, int historyDays)
        {
            TimeSpan billingPeriod = monthlyUsage.AnniversaryData.NextAnniversary.Subtract(monthlyUsage.AnniversaryData.LastAnniversary);
            TimeSpan billingPeriodRemaining = monthlyUsage.AnniversaryData.NextAnniversary.Subtract(monthlyUsage.AnniversaryData.UsageLastUpdated);
            TimeSpan billingPeriodElapsed = billingPeriod.Subtract(billingPeriodRemaining);

            if (planType != PlanType.ReducedOffPeak)
            {
                quotaRatio = 1;
            }

            int peakUsage = monthlyUsage.CountedUsage.Peak;
            int actualOffPeakUsage = monthlyUsage.CountedUsage.CountedOffPeak * quotaRatio;
            int countedOffPeakUsage = monthlyUsage.CountedUsage.CountedOffPeak;

            int peakQuota = 0;
            int offPeakQuota = 0;
            int peakQuotaUsed = 0;
            int offPeakQuotaUsed = 0;

            int quotaRemaining = 0;
            int quotaUsed = 0;
            int startQuota = 0;

            switch (planType)
            {
                case PlanType.ReducedOffPeak:
                    peakQuota = monthlyUsage.Quota;
                    offPeakQuota = peakQuota * quotaRatio;
                    peakQuotaUsed = peakUsage + countedOffPeakUsage;
                    offPeakQuotaUsed = peakQuotaUsed * quotaRatio;

                    quotaRemaining = peakQuota - peakQuotaUsed;
                    quotaUsed = peakQuotaUsed;
                    startQuota = peakQuota;
                    break;
                case PlanType.PayAsYouGo:
                    peakQuota = monthlyUsage.Quota;
                    offPeakQuota = monthlyUsage.Quota;
                    peakQuotaUsed = peakUsage + countedOffPeakUsage;
                    offPeakQuotaUsed = peakQuotaUsed;

                    quotaRemaining = peakQuota - peakQuotaUsed;
                    quotaUsed = peakQuotaUsed;
                    startQuota = peakQuota;
                    break;
                case PlanType.FixedOffPeak:
                    peakQuota = monthlyUsage.Quota - fixedOffPeakQuota;
                    offPeakQuota = fixedOffPeakQuota;
                    peakQuotaUsed = peakUsage;
                    offPeakQuotaUsed = countedOffPeakUsage;
                    if ((double)peakQuotaUsed / peakQuota < (double)offPeakQuotaUsed / offPeakQuota)
                    {
                        quotaRemaining = offPeakQuota - offPeakQuotaUsed;
                        quotaUsed = offPeakQuotaUsed;
                        startQuota = offPeakQuota;
                    }
                    else
                    {
                        quotaRemaining = peakQuota - peakQuotaUsed;
                        quotaUsed = peakQuotaUsed;
                        startQuota = peakQuota;
                    }

                    break;
                case PlanType.UnlimitedOffPeak:
                    peakQuota = monthlyUsage.Quota;
                    offPeakQuota = int.MaxValue;
                    peakQuotaUsed = peakUsage;
                    offPeakQuotaUsed = 0;

                    quotaRemaining = peakQuota - peakQuotaUsed;
                    quotaUsed = peakQuotaUsed;
                    startQuota = peakQuota;
                    break;
                default:
                    break;
            }
             
            GraphDataHandler graphDataHandler = new GraphDataHandler(monthlyUsage.PeakDownloadHistory, monthlyUsage.OffPeakDownloadHistory, monthlyUsage.PeakUploadHistory, monthlyUsage.OffPeakUploadHistory, historyDays, historyView, graphCountedOffPeak, commonScale, quotaRatio);
            graphDataHandler.CreateGraphData();

            return new UsageModel()
                {
                    FormattedNextAnniversaryToolTip = "Next Billing Month Starts:\n" + monthlyUsage.AnniversaryData.NextAnniversary.ToLongDateString(),
                    BillingSecondsMaximum = (int)billingPeriod.TotalSeconds,
                    BillingSecondsRemaining = (int)billingPeriodRemaining.TotalSeconds,
                    CurrentDownloadHistory = graphDataHandler.DownloadPrimaryData,
                    CurrentUploadHistory = graphDataHandler.UploadPrimaryData,
                    DailyAverageRemaining = CalculateDailyAverage(quotaRemaining, billingPeriodRemaining.TotalDays),
                    DailyAverageSoFar = CalculateDailyAverage(quotaUsed, billingPeriodElapsed.TotalDays),
                    DailyAverageStart = CalculateDailyAverage(startQuota, billingPeriod.TotalDays),
                    DownloadedToolTip = string.Format("Downloads:\nPeak: {0} MB\nOff Peak: {1} MB ({2} MB Counted)", monthlyUsage.DownloadStatistics.Peak, monthlyUsage.DownloadStatistics.CountedOffPeak * quotaRatio, monthlyUsage.DownloadStatistics.CountedOffPeak),
                    DownloadGraphMaximum = graphDataHandler.DownloadGraphMaximum,
                    ExcessCharges = monthlyUsage.ExcessCharges,
                    ExcessRate = monthlyUsage.ExcessRate,
                    HistoryStartDate = monthlyUsage.AnniversaryData.UsageLastUpdated.AddDays(-historyDays + 1),
                    FormattedBillingDaysRemaining = billingPeriodRemaining.TotalDays.ToString("0.00") + " days",
                    LastUpdated = monthlyUsage.AnniversaryData.UsageLastUpdated,
                    NextAnniversary = monthlyUsage.AnniversaryData.NextAnniversary,
                    OffPeakRemaining = offPeakQuota - offPeakQuotaUsed,
                    OffPeakRemainingColor = CalculateProgressColor(billingPeriod, billingPeriodRemaining, offPeakQuota - offPeakQuotaUsed, offPeakQuota),
                    OffPeakRemainingProgressMaximum = offPeakQuota,
                    PeakRemaining = peakQuota - peakQuotaUsed,
                    PeakRemainingColor = CalculateProgressColor(billingPeriod, billingPeriodRemaining, peakQuota - peakQuotaUsed, peakQuota),
                    PeakRemainingProgressMaximum = peakQuota,
                    PlanName = monthlyUsage.PlanName,
                    Quota = monthlyUsage.Quota,
                    SecondaryDownloadHistory = graphDataHandler.DownloadSecondaryData,
                    SecondaryUploadHistory = graphDataHandler.UploadSecondaryData,
                    TotalCountedDownloads = monthlyUsage.DownloadStatistics.Peak + (monthlyUsage.DownloadStatistics.CountedOffPeak * quotaRatio),
                    TotalCountedUploads = monthlyUsage.UploadStatistics.Peak + (monthlyUsage.UploadStatistics.CountedOffPeak * quotaRatio),
                    UploadedToolTip = string.Format("Uploads:\nPeak: {0} MB\nOff Peak: {1} MB ({2} MB Counted)", monthlyUsage.UploadStatistics.Peak, monthlyUsage.UploadStatistics.CountedOffPeak * quotaRatio, monthlyUsage.UploadStatistics.CountedOffPeak),
                    UploadGraphMaximum = graphDataHandler.UploadGraphMaximum,
                    UsageReserve = CalculateUsageReserve(billingPeriod, billingPeriodRemaining, startQuota, quotaRemaining)
                };
        }

        /// <summary>Calculates the current usage status.</summary>
        /// <param name="billingPeriod">A System.TimeSpan representing the current billing period.</param>
        /// <param name="billingPeriodRemaining">A System.TimeSpan representing the current billing period remaining.</param>
        /// <param name="quota">The total quota.</param>
        /// <param name="quotaRemaining">The amount of quota remaining.</param>
        /// <returns>A Redmist.UsageMonitor.CustomControls.ProgressStatusColor representing the current usage status.</returns>
        private static ProgressStatusColor CalculateProgressColor(TimeSpan billingPeriod, TimeSpan billingPeriodRemaining, int quota, int quotaRemaining)
        {
            double billingPeriodPercent = billingPeriodRemaining.TotalSeconds / billingPeriod.TotalSeconds;
            double quotaPercent = (double)quotaRemaining / quota;
            double usageRatio = quotaPercent / billingPeriodPercent * 100;
            if (usageRatio >= 100)
            {
                return ProgressStatusColor.Green;
            }
            
            if (usageRatio >= 90)
            {
                return ProgressStatusColor.Yellow;
            }

            return ProgressStatusColor.Red;
        }

        /// <summary>Calculates the amount of usage relative to the remaining billing period time.</summary>
        /// <param name="billingPeriod">A System.TimeSpan representing the current billing period.</param>
        /// <param name="billingPeriodRemaining">A System.TimeSpan representing the current billing period remaining.</param>
        /// <param name="quota">The total quota.</param>
        /// <param name="quotaRemaining">The amount of quota remaining.</param>
        /// <returns>The amount of usage relative to the remaining billing period time.</returns>
        private static int CalculateUsageReserve(TimeSpan billingPeriod, TimeSpan billingPeriodRemaining, int quota, int quotaRemaining)
        {
            double percentTimeRemaining = billingPeriodRemaining.TotalSeconds / billingPeriod.TotalSeconds;
            int zeroPointQuota = (int)(percentTimeRemaining * quota);
            return quotaRemaining - zeroPointQuota;
        }

        /// <summary>Calculates daily average quota.</summary>
        /// <param name="quota">A value representing a usage quota.</param>
        /// <param name="days">The number of days.</param>
        /// <returns>The average quota per day.</returns>
        private static int CalculateDailyAverage(int quota, double days)
        {
            if (days < 1)
            {
                days = 1;
            }

            return Convert.ToInt32(quota / days);
        }
    }
}
